/*
 * Licensed to the 南京汇海交通科技有限公司
 *
 * Copyright (c) 2025.
 * All rights reserved.
 *
 * 本软件属南京汇海交通科技有限公司所有，在未获得南京汇海交通科技有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.chframework.boot.web.event;

import cn.hutool.core.date.DatePattern;
import com.chframework.boot.web.startup.ApplicationReadyStartup;
import com.chframework.boot.web.support.BaseUrlBuilder;
import com.chframework.common.thread.ParallelHelper;
import com.chframework.common.thread.wrapper.BenchmarksFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.boot.context.event.ApplicationStartingEvent;
import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;

import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
import java.util.concurrent.ExecutorService;

/**
 * springboot 启动过程 事件监听处理
 */
@Slf4j
@Configuration(proxyBeanMethods = false)
public class ApplicationEventAdvice implements ApplicationListener<ApplicationEvent> {

    private static String baseUrl;

    @Override
    public void onApplicationEvent(@NonNull ApplicationEvent applicationEvent) {
        if (applicationEvent instanceof ApplicationStartingEvent) {
            log.debug("springboot应用启动且未作任何处理（除listener注册和初始化）的时候发送ApplicationStartingEvent");
        } else if (applicationEvent instanceof ApplicationEnvironmentPreparedEvent) {
            log.debug("确定springboot应用使用的Environment且context创建之前发送这个事件");
        } else if (applicationEvent instanceof ApplicationPreparedEvent) {
            log.debug("context已经创建且没有refresh发送个事件");
        } else if (applicationEvent instanceof ApplicationStartedEvent) {
            log.debug("context已经refresh且application and command-line runners（如果有） 调用之前发送这个事件");
        } else if (applicationEvent instanceof ApplicationReadyEvent) {
            log.debug("在application runners and command-line runners （如果有）执行完后，才会发送这个事件，此时应用已经启动完毕。这个事件比较常用，常常在系统启动完后做一些初始化操作");
            if (baseUrl != null) {
                return;
            }

            ApplicationReadyEvent applicationReadyEvent = (ApplicationReadyEvent) applicationEvent;
            ApplicationContext applicationContext = applicationReadyEvent.getApplicationContext();
            WebApplicationType webApplicationType = applicationReadyEvent.getSpringApplication().getWebApplicationType();

            if (webApplicationType == WebApplicationType.REACTIVE) {
                log.info("WebApplicationType.REACTIVE - ApplicationReadyEvent 持有的 ApplicationContext对象 是 {} ", applicationContext.getClass().getName());
                if (applicationContext instanceof ReactiveWebServerApplicationContext) {
                    WebFluxProperties webFluxProperties = applicationContext.getBean(WebFluxProperties.class);
                    baseUrl = BaseUrlBuilder.createBaseUrlAboutSpringWeb(applicationContext, (serverProperties) -> webFluxProperties.getBasePath());
                }
            } else if (webApplicationType == WebApplicationType.SERVLET) {
                log.info("WebApplicationType.SERVLET - ApplicationReadyEvent 持有的 ApplicationContext对象 是 {} ", applicationContext.getClass().getName());
                if (applicationContext instanceof ServletWebServerApplicationContext) {
                    baseUrl = BaseUrlBuilder.createBaseUrlAboutSpringWeb(applicationContext, (serverProperties) -> serverProperties.getServlet().getContextPath());
                }
            } else {
                log.info("WebApplicationType.NONE - ApplicationReadyEvent 持有的 ApplicationContext对象 是 {} ", applicationContext.getClass().getName());
            }

            if (baseUrl != null) {
                ExecutorService threadPool = applicationContext.getBean(ExecutorService.class);
                ParallelHelper.setThreadPool(threadPool);
                BenchmarksFuture.setThreadPool(threadPool);

                String applicationName = applicationContext.getId();
                DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN);
                String bootTime = Instant.ofEpochMilli(applicationContext.getStartupDate())
                        .atZone(ZoneId.systemDefault())
                        .toLocalDateTime()
                        .format(dateTimeFormatter);

                log.info("------- SpringBoot工程 {} 已经启动 ------- {}启动时间 {}" + " 开始执行初始化工作！！！！！！！！！",
                        applicationName,
                        StringUtils.isBlank(baseUrl) ? StringUtils.EMPTY : String.format("访问地址 %s ", baseUrl),
                        bootTime);

                // 开始执行初始化工作！！！！！！
                ApplicationReadyStartup startup = applicationContext.getBean(ApplicationReadyStartup.class);
                if (!Objects.isNull(startup)) {
                    threadPool.execute(() -> startup.applicationReadyInitHandler(applicationContext));
                }
            }
        } else if (applicationEvent instanceof ApplicationFailedEvent) {
            log.error("springboot应用启动失败后产生这个事件");
        }
    }

}
